package fatworm.planner;

import java.util.LinkedList;

import fatworm.query.constant.*;
import fatworm.query.expression.*;
import fatworm.query.*;
import fatworm.util.Lib;
import fatworm.concept.DataType.SqlValue;
import fatworm.concept.datatype.*;
import fatworm.parser.syntree.*;
import fatworm.tx.*;

public class Translator {
	
	private Transaction tx;
	
	public Translator(Transaction tx) {
		this.tx = tx;
	}
	
	private Expression convert(ValueAopNode vnode) {
		return new OpExpression(vnode.oper.getOper(), convert(vnode.left), convert(vnode.right));
	}
	
	private Expression convert(ValueConstNode vnode) {
		SqlValue v = vnode.value;
		Constant c = null;
		if (v instanceof FatBoolean.BooleanValue)
			c = new BooleanConstant(((FatBoolean.BooleanValue)v).value);
		else if (v instanceof FatInt.IntValue)
			c = new IntConstant(((FatInt.IntValue)v).value);
		else if (v instanceof FatString.StringValue) 
			c = new StringConstant(((FatString.StringValue)v).value);
		else if (v instanceof FatFloat.FloatValue)
			c = new FloatConstant(((FatFloat.FloatValue)v).value);
		else if (v instanceof FatDateTime.DateTimeValue)
			c = new DateConstant(((FatDateTime.DateTimeValue)v).value.toString());
		else if (v.toString().equals("NULL"))
			c = new NullConstant();
			
		Lib.assertTrue(c != null);
		return new ConstantExpression(c);
	}
	
	private Expression convert(ValueFieldNode vnode) {
		return new FieldNameExpression(vnode.fieldName);
	}
	
	private Expression convert(ValueFunctionNode vnode) {
		return new FieldNameExpression(vnode.toString());
	}
	
	private Expression convert(ValueParenNode vnode) {
		return convert(vnode.value);
	}
	
	private Expression convert(ValueSubqueryNode vnode) {
		return new SubqueryExpression(convert(vnode.select));
	}
	
	public Expression convert(ValueNode vnode) {
		if (vnode instanceof ValueAopNode) 
			return convert((ValueAopNode)vnode);
		else if (vnode instanceof ValueConstNode) 
			return convert((ValueConstNode)vnode);
		else if (vnode instanceof ValueFieldNode)
			return convert((ValueFieldNode)vnode);
		else if (vnode instanceof ValueFunctionNode) 
			return convert((ValueFunctionNode)vnode);
		else if (vnode instanceof ValueParenNode) 
			return convert((ValueParenNode)vnode);
		else if (vnode instanceof ValueSubqueryNode) 
			return convert((ValueSubqueryNode)vnode);
		else
			Lib.assertNotReached();
		return null;
	}
	
/*-------------------------------------------------------------------------------*/
	
	private Plan convert(SelectNode qnode) { 
		if (qnode.fromList == null)
			return null;
		
		LinkedList<Plan> planlist = new LinkedList<Plan>();
		LinkedList<SelectNode.TableRef> tablelist = qnode.fromList;
		
		for (int i = 0; i < tablelist.size(); i++) {
			SelectNode.TableRef ref = tablelist.get(i);
			if (ref instanceof SelectNode.SimpleRef)
				planlist.add(new TablePlan(((SelectNode.SimpleRef)ref).tableName, tx));
			else if (ref instanceof SelectNode.SubqueryRef) {
				SelectNode.SubqueryRef sref = (SelectNode.SubqueryRef)ref;
				Plan subp = convert(sref.subquery);
				planlist.add(subp);
			}
		}
		
		if (planlist.size() == 0)
			return null;
		
		Plan res = planlist.get(0);
		for (int i = 1; i < planlist.size(); i++)
			res = new ProductPlan(res, planlist.get(i));
		
		LinkedList<ValueNode>	vlist = qnode.selectList;
		if (qnode.hasGroupBy()) {
			for (int i = 0; i < vlist.size(); i++)
				if (vlist.get(i) instanceof ValueFunctionNode) {
					String fieldname = qnode.groupByList.get(0);
					String cmd = ((ValueFunctionNode)vlist.get(i)).func.toString();
					String name = ((ValueFunctionNode)vlist.get(i)).fieldName;
					Term term = null;
					if (qnode.hasHaving())
						term = convert(qnode.havingCond);
					res = new GroupByPlan(res, fieldname, cmd, name, new Predicate(term));
				}
		}
		
		if (qnode.hasWhere()) {
			Term term = convert(qnode.whereCond);
			res = new SelectPlan(res, new Predicate(term));
		}
		
		LinkedList<ValueNode> valuelist = qnode.selectList;
		LinkedList<Expression> elist = new LinkedList<Expression>();
		for (int i = 0; i < valuelist.size(); i++)
			elist.add(convert(valuelist.get(i)));
		if (elist.size() > 0)
			res = new ProjectPlan(res, elist);
		
		for (int i = 0; i < valuelist.size(); i++) 
			if (valuelist.get(i).alias != null)
				res = new ExtendPlan(res, elist.get(i).toString(), valuelist.get(i).alias);
		
		if (qnode.distinct)
			res = new DistinctPlan(res);
		
		if (qnode.hasOrderBy()) 
			res = new SortPlan(res, qnode.orderByList.get(0), qnode.order.getOrder());		
		
		return res;   
	}
	
	public Plan convert(QueryNode qnode) {
		System.out.println("1111");
		if (qnode instanceof SelectNode)
			return convert((SelectNode)qnode);
		else {System.out.println("XXXX");
			Lib.assertNotReached();}
		return null;   
	}
	
/*-------------------------------------------------------------------------------*/
	
	private Term convert(WhereAndNode wnode) {
		return new MultiTerms(convert(wnode.left), convert(wnode.right), 1);
	}
	
	private Term convert(WhereAtomicNode wnode) {
		return null;
	}
	
	private Term convert(WhereCopAllNode wnode) {
		return new OpAllTerm(convert(wnode.value), new SubqueryExpression(convert(wnode.subquery)), wnode.relop.getRelop());
	}
	
	private Term convert(WhereCopAnyNode wnode) {
		return new OpAnyTerm(convert(wnode.value), new SubqueryExpression(convert(wnode.subquery)), wnode.relop.getRelop());
	}
	
	private Term convert(WhereCopNode wnode) {
		return new OpTerm(convert(wnode.left), convert(wnode.right), wnode.relop.getRelop());
	}
	
	private Term convert(WhereExistsNode wnode) {
		return new ExistsTerm(new SubqueryExpression(convert(wnode.subquery)));
	}
	
	private Term convert(WhereInNode wnode) {
		return new InTerm(convert(wnode.value), new SubqueryExpression(convert(wnode.subquery)));
	}
	
	private Term convert(WhereNotNode wnode) {
		return new NotTerm(convert(wnode.notwhere));
	}
	
	private Term convert(WhereOrNode wnode) {
		return new MultiTerms(convert(wnode.left), convert(wnode.right), 0);
	}
	
	private Term convert(WhereParenNode wnode) {
		return convert(wnode.where);
	}
	
	public Term convert(WhereNode wnode) {
		if (wnode instanceof WhereAndNode)
			return convert((WhereAndNode)wnode);
		else if (wnode instanceof WhereAtomicNode)
			return convert((WhereAtomicNode)wnode);
		else if (wnode instanceof WhereCopAllNode)
			return convert((WhereCopAllNode)wnode);
		else if (wnode instanceof WhereCopAnyNode)
			return convert((WhereCopAnyNode)wnode);
		else if (wnode instanceof WhereCopNode)
			return convert((WhereCopNode)wnode);
		else if (wnode instanceof WhereExistsNode)
			return convert((WhereExistsNode)wnode);
		else if (wnode instanceof WhereInNode)
			return convert((WhereInNode)wnode);
		else if (wnode instanceof WhereNotNode)
			return convert((WhereNotNode)wnode);
		else if (wnode instanceof WhereOrNode)
			return convert((WhereOrNode)wnode);
		else if (wnode instanceof WhereParenNode)
			return convert((WhereParenNode)wnode);
		else
			Lib.assertNotReached();
		return null;
	}
	
}